package rsj.proxy;


import com.rlyy.aibase.utils.base.ThinkException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.stereotype.Component;
import rsj.proxy.strategy.IExecuteStrategy;

import javax.sql.DataSource;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 代理逻辑
 *
 * @author yuanqinglong
 * @since 2021/4/26 9:04
 */
@Component
public class ProxyInvokeHandler implements InvocationHandler {

    /**
     * 数据源
     */
    @Autowired
    private DataSource dataSource;

    /**
     * 每个SQL注解的处理策略
     */
    private final Map<Class<?>, IExecuteStrategy> executeStrategyMap;


    public ProxyInvokeHandler(List<IExecuteStrategy> iExecuteStrategies) {
        executeStrategyMap = iExecuteStrategies.stream().collect(Collectors.toMap(IExecuteStrategy::getSupportAnnotationType, Function.identity()));
    }

    /**
     * 代理逻辑
     *
     * @param proxy  代理对象
     * @param method 被代理方法
     * @param args   参数
     * @return sql执行结果
     * @throws Throwable 异常信息
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }
        IExecuteStrategy iExecuteStrategy = null;
        for (Annotation declaredAnnotation : method.getDeclaredAnnotations()) {
            // 根据方法使用的注解@Select 、@Update找到对应注解处理器执行sql
            iExecuteStrategy = executeStrategyMap.get(declaredAnnotation.annotationType());
            if (Objects.nonNull(iExecuteStrategy)) {
                break;
            }
        }
        if (Objects.isNull(iExecuteStrategy)) {
            throw new ThinkException("无法执行(" + method.getName() + ")方法");
        }
        // 执行对应查询策略逻辑
        return this.doExecuteStrategy(iExecuteStrategy, method, args);
    }


    /**
     * 执行SQL 并判断是否是开启事务的操作、释放未开启事务的数据库连接
     *
     * @param iExecuteStrategy iExecuteStrategy
     * @param method           执行方法
     * @param args             参数
     */
    private Object doExecuteStrategy(IExecuteStrategy iExecuteStrategy, Method method, Object[] args) throws Exception {
        // 从Spring事务管理器获取连接、同一个线程获取的连接相同、支持Spring声明式事务@Transaction异常回滚
        Connection connection = DataSourceUtils.getConnection(dataSource);
        try {
            return iExecuteStrategy.executeStrategy(connection, method, args);
        } finally {
            // 释放非事务操作数据库连接
            // 如果开启了事务，此操作Spring通过判断不会将连接关闭，执行完所有操作后Spring自动关闭
            DataSourceUtils.releaseConnection(connection, dataSource);
        }
    }


}
